﻿//module;
/**
 * @author: rayzhang
 * @desc:
 *  键值对存取, 类似 QSettings, 这个类可以扩展自定义存储方式，比如存储到数据库，文件，网络等。
 *  Storage::initTypeStreamOperators();
 *
    Storage *storage = new BinaryStorage("key");
    // user define struct
    struct Data{
       QString name;
       QString values;
    }
    Q_DECLARE_METATYPE(Data)

    QMap<QString, QVector<Data>> dataMap;
    qRegisterMetaTypeStreamOperators<QMap<QString, QVector<Data>>>("QMap<QString, QVector<Data>>");
    // set
    storage->setItem("dataMap", dataMap);
    // get
    QMap<QString, QVector<Data>> dataMap;
    storage->getItemTo("dataMap", dataMap);
    storage->getItem<QMap<QString, QVector<Data>>>("dataMap);


    storage->setItem("key2", 123);
    int value2 = storage.getItem("key2").toInt();

 */
#pragma warning(disable:5260)
#include <qcoreapplication.h>
#include <QFile>
#include <QDataStream>
#include <QStandardPaths>
#include <QDir>
#include <QVariant>
#include <QFile>
#include <QDataStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include "../libflow_global.h"
//export module libflow:storage;
/*export */namespace flow
{
class Storage
{
public:
    Storage(const QString& key)
        : _key(key)
    { }

    virtual ~Storage() { }

    virtual bool setItem(const QString& key, const QVariant& value)                       = 0;
    virtual QVariant getItem(const QString& key, const QVariant& defaultValue = {}) const = 0;
    virtual bool saveData()                                                               = 0;
    auto key() const { return _key; }
    virtual bool contains(const QString& key) = 0;
    auto errorString() const { return _lastError; }

    static void initTypeStreamOperators()
    {
//
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        qRegisterMetaTypeStreamOperators<QMap<QString, QString>>("QMap<QString, QString>");
#else
        QMetaType::registerConverter<QMap<QString, QString>, QVariant>([](const QMap<QString, QString>& t) {
            return QVariant::fromValue(t);
        });
#endif
    }

protected:
    void setLastError(const QString& lastError) { _lastError = lastError; }

private:
    QString _key = "global";
    QString _lastError;
};

class JsonStorage : public Storage
{
public:
    JsonStorage(const QString& key)
        : Storage(key)
    {
        // 打开文件
        const QString appName = QCoreApplication::applicationName();
        const QString homeDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
        // 创建缓存目录
        const QString cacheDirPath = QString("%1/%2/.cache").arg(homeDir).arg(appName);
        const QDir dir;
        if (!dir.exists(cacheDirPath)) {
            dir.mkpath(cacheDirPath);
        }
        // 打开文件
        const QString filename = cacheDirPath + ".session.json";
        file.setFileName(filename);
        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            // 读取已存在的数据
            const QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
            data                    = doc.toVariant().toMap();
        }
        else {
            setLastError(file.errorString());
        }
    }
    ~JsonStorage() override
    {
        if (file.isOpen()) {
            file.close();
        }
    }
    bool contains(const QString& key) override { return data.contains(key); }
    bool setItem(const QString& key, const QVariant& value) override
    {
        data[key] = QJsonValue::fromVariant(value);
        dirty     = true; // 标记数据已更改
        return saveData();
    }
    QVariant getItem(const QString& key, const QVariant& defaultValue = {}) const override { return data.value(key, defaultValue); }
    bool saveData() override
    {
        bool isOk = false;
        if (dirty && file.isOpen()) { // 只有在数据更改时才保存
            file.seek(0);             // 回到文件开头
            file.resize(0);           // 清空文件
            const QJsonDocument doc = QJsonDocument::fromVariant(data);
            file.write(doc.toJson(QJsonDocument::Compact));
            isOk  = file.flush();
            dirty = false; // 数据已保存，重置 dirty 标记
        }
        else {
            setLastError(QString("file is open: ").arg(file.isOpen()));
        }
        return isOk;
    }

private:
    QFile file;
    QVariantMap data;
    bool dirty = false; // 数据是否已更改的标记
};

class BinaryStorage : public Storage
{
public:
    BinaryStorage(const QString& key)
        : Storage(key)
    {
        const QString appName = QCoreApplication::applicationName();
        const QString homeDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
        // 创建缓存目录
        const QString cacheDirPath = QString("%1/%2/.cache").arg(homeDir).arg(appName);
        const QDir dir;
        if (!dir.exists(cacheDirPath)) {
            dir.mkpath(cacheDirPath);
        }
        // 打开文件
        const QString filename = cacheDirPath + "/.session.dat";
        file.setFileName(filename);
        if (file.open(QIODevice::ReadWrite)) {
            // 读取已存在的数据
            QDataStream in(&file);
            in >> data;
        }
        else {
            setLastError(file.errorString());
        }
    }
    ~BinaryStorage() override
    {
        // 关闭文件
        if (file.isOpen()) {
            file.close();
        }
    }
    bool contains(const QString& key) override { return data.contains(key); }
    bool setItem(const QString& key, const QVariant& value) override
    {
        data[key] = value;
        dirty     = true; // 标记数据已更改
        return saveData();
    }
    QVariant getItem(const QString& key, const QVariant& defaultValue = {}) const override { return data.value(key, defaultValue); }
    bool saveData() override
    {
        try {
            bool isOk = false;
            if (dirty && file.isOpen()) { // 只有在数据更改时才保存
                file.seek(0);             // 回到文件开头
                file.resize(0);           // 清空文件
                QDataStream out(&file);
                out << data;
                isOk  = file.flush(); // 确保数据已写入文件
                dirty = false;        // 数据已保存，重置 dirty 标记
            }
            else {
                setLastError(QString("file is open: ").arg(file.isOpen()));
            }
            return isOk;
        }
        catch (...) {
            return false;
        }
    }

private:
    QFile file;
    QVariantMap data;
    bool dirty = false; // 数据是否已更改的标记
};

template <typename S>
    requires std::is_base_of_v<Storage, S>
class SessionStorage
{
public:
    SessionStorage(const QString& key)
        : _storage(std::make_shared<S>(key))
    { }

    template <typename T>
    bool setItem(const QString& key, const T& value)
    {
        return _storage->setItem(key, QVariant::fromValue(value));
    }

    template <typename T>
    T getItem(const QString& key, const T& defaultValue) const
        requires(!std::is_same_v<T, QVariant>)
    {
        using ValueType = std::decay_t<T>;
        if (_storage->contains(key)) {
            const auto& data = _storage->getItem(key, defaultValue);
            if (data.template canConvert<ValueType>()) {
                return data.template value<std::decay_t<ValueType>>();
            }
        }
        return false;
    }
    bool contains(const QString& key) { return _storage->contains(key); }
    QVariant getItem(const QString& key, const QVariant& defaultValue) const { return _storage->getItem(key, defaultValue); }

    template <typename T>
    bool getItemTo(const QString& key, T& value) const
    {
        using ValueType = std::decay_t<T>;
        if (_storage->contains(key)) {
            const auto& data = _storage->getItem(key);
            if (data.template canConvert<ValueType>()) {
                value = data.template value<std::decay_t<ValueType>>();
                return true;
            }
        }
        return false;
    }

    auto key() const { return _storage->key(); }

private:
    std::shared_ptr<S> _storage;
};

template <typename S>
    requires std::is_base_of_v<Storage, S>
using LocalStorage = SessionStorage<S>;
} // namespace flow
